#ifndef _FFCODEC_H_
#define _FFCODEC_H_

#include <stdint.h>
#include <pthread.h>
#include <iostream>

// 编码/解码
typedef enum {
    CODEC_TYPE_ENCODE = 0,
    CODEC_TYPE_DECODE,
} CodecControlOhosType;

// 格式大全
typedef enum {
    CODEC_FORMAT_UNKNOWN = -1,

    //音频编码
    CODEC_FORMAT_PCM = 1000,
    CODEC_FORMAT_AAC,

    //照片编码
    CODEC_FORMAT_JPEG = 1100,

    //视频编码
    CODEC_FORMAT_H264 = 1200,
    CODEC_FORMAT_H265,
    CODEC_FORMAT_MJPEG,

    //RGB排列
    CODEC_FORMAT_RGB = 2000,
    CODEC_FORMAT_BGR,

    CODEC_FORMAT_ARGB,
    CODEC_FORMAT_ABGR,

    CODEC_FORMAT_RGBA,
    CODEC_FORMAT_BGRA,

    //YUV排列
    CODEC_FORMAT_YUV420P = 2100,
    CODEC_FORMAT_YUV422P,

    CODEC_FORMAT_YVU420P,
    CODEC_FORMAT_YVU422P,

    CODEC_FORMAT_YUV420SP, // 即 NV12, UV交错排列
    CODEC_FORMAT_YUV422SP, // 即 NV16, UV交错排列

    CODEC_FORMAT_YVU420SP, // 即 NV21, VU交错排列
    CODEC_FORMAT_YVU422SP, // 即 NV61, VU交错排列

    CODEC_FORMAT_YUV400, // 灰度图
} CodecControlOhosFormat;

// YUV或RGB图像数据存储结构体
typedef struct {
    CodecControlOhosFormat format;
    uint32_t width;
    uint32_t height;
    uint32_t pb; //每像素字节数
    uint8_t *map;
} FFCodec_Map;

char* ffcodec_info(void);

/*
 *  视频编解码初始化
 *  参数:
 *      type: 编码还是解码
 *      format: 具体格式
 *      encodedFormat: 被编码格式,如编码h264需指定yuv格式
 *      groupSize: 一组图像中的图像数量,图像运动剧烈可以设小点,当然数据量也会变大
 *  返回: 控制句柄, NULL失败
 */
void* ffcodec_init(
        CodecControlOhosType type,
        CodecControlOhosFormat inputFormat,
        CodecControlOhosFormat outputFormat,
        uint32_t width = 720,
        uint32_t height = 480,
        uint32_t fps = 30,
        uint32_t groupSize = 20);

void ffcodec_deinit(void *handle);

/*
 *  视频解码
 *  参数:
 *      buff: 一帧待解码数据
 *      buffSize: buff字节长度
 *      yuvMap: 数组yuvMap[3]的首地址,用于返回yuv数据(不需要用后释放)
 *      rgbMap: 返回rgb数据,需提前配置好map、width、height、format参数(自行管理内存分配和释放)
 *  返回: 0成功 -1失败
 */
int32_t ffcodec_decode(
        void *handle,
        uint8_t *buff,
        uint32_t buffSize,
        FFCodec_Map yuvMap[3],
        FFCodec_Map *rgbMap);

/*
 *  视频编码
 *  参数:
 *      yuvMap: 数组yuvMap[3]的首地址,存储yuv数据,需自行分配好内存并设置正确width、height、format参数
 *      frameBuff: 返回编码后数据指针(系统内部指针),请在下次调用该函数前使用完(不需要用后释放)
 *      frameBuffSize: 返回frameBuff数据长度
 *  返回: 0成功 -1失败
 */
int32_t ffcodec_encode(
    void *handle,
    FFCodec_Map yuvMap[3],
    uint8_t **frameBuff,
    uint32_t *frameBuffSize);

//yuv转rgb,返回0成功,rgbMap需提前配置好map、width、height、format参数
int32_t ffcodec_YUV_to_RGB(FFCodec_Map yuvMap[3], FFCodec_Map *rgbMap);

// ----- 其它工具方法 -----

//查找h264/265帧位置,返回offset,-1失败
int32_t h26xFindFrame(const uint8_t *buff, uint32_t buffLen, int32_t *frameSize, char *type);

//移动缓冲区,配合H26xFindFrame()使用
void h26xMoveBuff(uint8_t *buff, int32_t &buffLen, int32_t &buffOffset, int32_t &frameSize);

//基于上面两方法封装针对h26x数据流的fifo
class H26xFiFo {
public:
    H26xFiFo(int frameSize, int frameCount);
    ~H26xFiFo();
    int Set(uint8_t *dat, int len);
    int Get(uint8_t *dat, int len);
private:
    int buffSize = 0;
    uint8_t* buff = NULL;
    int index = 0;
    int headLen = 0;
    int tailLen = 0;
    pthread_mutex_t lock;
};

/***** 使用示例 *****
int32_t ret;

uint32_t buffSize = 1024 * 1024;
uint8_t *buff = new uint8_t[buffSize];
int32_t buffLen = 0;
int32_t buffOffset = 0;
int32_t frameSize = 0;
char type = 0;

while (1)
{
    h26xMoveBuff(buff, buffLen, buffOffset, frameSize);

    ret = read(fd, buff + buffLen, buffSize - buffLen);
    if (ret < frameSize - buffLen)
        lseek(fd, 0, SEEK_SET);
    buffLen += ret;

    buffOffset = h26xFindFrame(buff, buffLen, &frameSize, &type);
    if (buffOffset < 0)
        continue;

    ret = ffcodec_decode(handle, buff + buffOffset, buffLen, ...);
    ... ...
    ... ...
}
*/

#endif

